package com.campus.payment.service;

import cn.hutool.core.date.DateUtil;
import com.campus.payment.config.WechatPayConfig;
import com.campus.payment.dto.*;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RSAPublicKeyNotificationConfig;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.*;
import com.wechat.pay.java.service.profitsharing.ProfitsharingService;
import com.wechat.pay.java.service.profitsharing.model.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 微信支付服务类
 *
 * @author ZhaoYuJie
 * @since 2025-01-15
 */
@Slf4j
@Service
public class WechatPayService {
    
    @Value("${wechat.appid}")
    private String appid;

    @Autowired
    private WechatPayConfig wechatPayConfig;

    private JsapiServiceExtension jsapiService;
    private ProfitsharingService profitsharingService;

    /**
     * 初始化微信支付服务
     */
    @PostConstruct
    public void init() {
        try {
            Config config = new RSAPublicKeyConfig.Builder()
                    .merchantId(wechatPayConfig.getMerchantId())
                    .privateKeyFromPath(wechatPayConfig.getPrivateKeyPath())
                    .merchantSerialNumber(wechatPayConfig.getMerchantSerialNumber())
                    .publicKeyFromPath(wechatPayConfig.getPublicKeyPath())
                    .publicKeyId(wechatPayConfig.getPublicKeyId())
                    .apiV3Key(wechatPayConfig.getApiV3Key())
                    .build();

            // 初始化JSAPI服务
            jsapiService = new JsapiServiceExtension.Builder()
                    .config(config)
                    .signType("RSA")
                    .build();

            // 初始化分账服务
            profitsharingService = new ProfitsharingService.Builder()
                    .config(config)
                    .build();

            log.info("微信支付服务初始化成功");
        } catch (Exception e) {
            log.error("微信支付服务初始化失败", e);
            throw new RuntimeException("微信支付服务初始化失败", e);
        }
    }

    /**
     * 创建微信支付订单
     *
     * @param request 支付订单请求
     * @return 微信支付参数
     */
    public PayOrderResponseDTO createPayOrder(PayOrderRequestDTO request) {
        try {
            // 生成订单商品描述
            String description = generateOrderDescription(request.getOrderGoods());

            PrepayRequest prepayRequest = new PrepayRequest();
            // 设置小程序APPID
            prepayRequest.setSpAppid(this.appid);
            // 设置服务商商户号
            prepayRequest.setSpMchid(wechatPayConfig.getMerchantId());
            // 设置子商户号
            prepayRequest.setSubMchid(request.getSubMchid());
            // 设置商品描述
            prepayRequest.setDescription(description);
            // 设置商户订单号
            prepayRequest.setOutTradeNo(request.getOrderId());
            // 设置订单失效时间（15分钟后）
            prepayRequest.setTimeExpire(DateUtil.format(DateUtil.offsetMinute(new Date(), 15), "yyyy-MM-dd'T'HH:mm:ssXXX"));
            // 设置支付回调地址
            prepayRequest.setNotifyUrl(wechatPayConfig.getNotifyUrl());

            // 设置分账信息
            SettleInfo settleInfo = new SettleInfo();
            settleInfo.setProfitSharing(true);
            prepayRequest.setSettleInfo(settleInfo);

            // 设置订单金额（元转分）
            Amount amount = new Amount();
            amount.setTotal(request.getActualPay().multiply(new BigDecimal("100")).intValue());
            prepayRequest.setAmount(amount);

            // 设置支付者信息
            Payer payer = new Payer();
            payer.setSpOpenid(request.getUserOpenid());
            prepayRequest.setPayer(payer);

            // 调用微信支付接口
            PrepayWithRequestPaymentResponse response = jsapiService.prepayWithRequestPayment(prepayRequest, this.appid);
            log.info("微信支付订单创建成功，订单号：{}", request.getOrderId());
            
            // 转换为DTO
            PayOrderResponseDTO responseDTO = new PayOrderResponseDTO();
            responseDTO.setTimeStamp(response.getTimeStamp());
            responseDTO.setNonceStr(response.getNonceStr());
            responseDTO.setPackageVal(response.getPackageVal());
            responseDTO.setSignType(response.getSignType());
            responseDTO.setPaySign(response.getPaySign());
            
            return responseDTO;
        } catch (Exception e) {
            log.error("创建微信支付订单失败，订单号：{}", request.getOrderId(), e);
            throw new RuntimeException("创建微信支付订单失败", e);
        }
    }

    /**
     * 生成订单商品描述
     *
     * @param orderGoodsList 订单商品列表
     * @return 商品描述
     */
    private String generateOrderDescription(List<PayOrderRequestDTO.OrderGoodsDTO> orderGoodsList) {
        if (orderGoodsList == null || orderGoodsList.isEmpty()) {
            return "校园点餐订单";
        }

        // 取前3个商品名称
        String goodsNames = orderGoodsList.stream()
                .limit(3)
                .map(PayOrderRequestDTO.OrderGoodsDTO::getName)
                .collect(Collectors.joining("、"));

        if (orderGoodsList.size() > 3) {
            goodsNames += "等" + orderGoodsList.size() + "件商品";
        }

        return goodsNames;
    }

    /**
     * 关闭订单
     *
     * @param request 关闭订单请求
     */
    public void closeOrder(CloseOrderRequestDTO request) {
        try {
            CloseOrderRequest closeRequest = new CloseOrderRequest();
            closeRequest.setSpMchid(wechatPayConfig.getMerchantId());
            closeRequest.setSubMchid(request.getSubMchid());
            closeRequest.setOutTradeNo(request.getOrderId());

            jsapiService.closeOrder(closeRequest);
            log.info("微信支付订单关闭成功，订单号：{}", request.getOrderId());
        } catch (Exception e) {
            log.error("关闭微信支付订单失败，订单号：{}", request.getOrderId(), e);
            throw new RuntimeException("关闭微信支付订单失败", e);
        }
    }

    /**
     * 查询订单支付状态
     *
     * @param request 查询请求
     * @return 订单信息
     */
    public Transaction queryOrderByOutTradeNo(OrderStatusQueryDTO request) {
        try {
            QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
            queryRequest.setSpMchid(wechatPayConfig.getMerchantId());
            queryRequest.setSubMchid(request.getSubMchid());
            queryRequest.setOutTradeNo(request.getOutTradeNo());

            Transaction transaction = jsapiService.queryOrderByOutTradeNo(queryRequest);
            log.info("查询微信支付订单成功，订单号：{}，支付状态：{}", request.getOutTradeNo(), transaction.getTradeState());
            return transaction;
        } catch (Exception e) {
            log.error("查询微信支付订单失败，订单号：{}", request.getOutTradeNo(), e);
            throw new RuntimeException("查询微信支付订单失败", e);
        }
    }

    /**
     * 查询订单支付状态
     * @param outTradeNo 商户订单号
     * @param subMchid 子商户号
     * @return 支付状态
     */
    public String queryOrderStatus(String outTradeNo, String subMchid) {
        try {
            QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
            request.setSpMchid(wechatPayConfig.getMerchantId());
            request.setSubMchid(subMchid);
            request.setOutTradeNo(outTradeNo);
            
            Transaction transaction = jsapiService.queryOrderByOutTradeNo(request);
            return transaction.getTradeState().name();
        } catch (Exception e) {
            throw new RuntimeException("查询订单支付状态失败: " + e.getMessage(), e);
        }
    }
     
    /**
     * 处理微信支付回调
     * @param callbackDTO 回调数据
     * @return 处理结果
     */
    public PaymentCallbackResult handlePaymentCallback(PaymentCallbackDTO callbackDTO) {
        try {
            // 构造 RequestParam
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(callbackDTO.getWechatpaySerial())
                    .nonce(callbackDTO.getWechatpayNonce())
                    .signature(callbackDTO.getWechatpaySignature())
                    .timestamp(callbackDTO.getWechatpayTimestamp())
                    .body(callbackDTO.getRequestBody())
                    .build();
            
            // 使用微信支付公私钥初始化 NotificationConfig
            NotificationConfig config = new RSAPublicKeyNotificationConfig.Builder()
                    .publicKeyFromPath(wechatPayConfig.getPublicKeyPath())
                    .publicKeyId(wechatPayConfig.getPublicKeyId())
                    .apiV3Key(wechatPayConfig.getApiV3Key())
                    .build();
            
            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(config);
            
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            
            // 处理支付成功的回调
            if ("SUCCESS".equals(transaction.getTradeState().name())) {
                String outTradeNo = transaction.getOutTradeNo();
                log.info("微信支付回调成功，订单号：{}，微信支付订单号：{}", outTradeNo, transaction.getTransactionId());
                
                PaymentCallbackResult result = new PaymentCallbackResult();
                result.setSuccess(true);
                result.setOutTradeNo(outTradeNo);
                result.setTransactionId(transaction.getTransactionId());
                return result;
            } else {
                log.warn("微信支付回调，支付状态异常：{}，订单号：{}", transaction.getTradeState(), transaction.getOutTradeNo());
                PaymentCallbackResult result = new PaymentCallbackResult();
                result.setSuccess(false);
                result.setOutTradeNo(transaction.getOutTradeNo());
                return result;
            }
            
        } catch (Exception e) {
            log.error("处理微信支付回调失败", e);
            PaymentCallbackResult result = new PaymentCallbackResult();
            result.setSuccess(false);
            return result;
        }
    }
    
    /**
     * 支付回调结果
     */
    public static class PaymentCallbackResult {
        private boolean success;
        private String outTradeNo;
        private String transactionId;
        
        public boolean isSuccess() {
            return success;
        }
        
        public void setSuccess(boolean success) {
            this.success = success;
        }
        
        public String getOutTradeNo() {
            return outTradeNo;
        }
        
        public void setOutTradeNo(String outTradeNo) {
            this.outTradeNo = outTradeNo;
        }
        
        public String getTransactionId() {
            return transactionId;
        }
        
        public void setTransactionId(String transactionId) {
            this.transactionId = transactionId;
        }
    }

    /**
     * 添加分账接收方
     *
     * @param subMchid 子商户号
     * @param type 接收方类型：MERCHANT_ID-商户号，PERSONAL_OPENID-个人OpenID
     * @param account 接收方账号
     * @param name 接收方名称
     * @param relationshipType 与分账方的关系类型
     * @return 添加结果
     */
    public AddReceiverResponse addProfitSharingReceiver(String subMchid, String type, String account, String name, String relationshipType) {
        try {
            AddReceiverRequest request = new AddReceiverRequest();
            request.setSubMchid(subMchid);
            request.setAppid(appid);
            //根据type参数和ReceiverType枚举类的值进行匹配
            request.setType(ReceiverType.valueOf(type));
            request.setAccount(account);
            if(StringUtils.isNotBlank(name)){
                request.setName(name);
            }
            request.setRelationType(ReceiverRelationType.valueOf(relationshipType));

            AddReceiverResponse response = profitsharingService.addReceiver(request);
            log.info("添加分账接收方成功，接收方账号：{}", account);
            return response;
        } catch (Exception e) {
            log.error("添加分账接收方失败，接收方账号：{}", account, e);
            throw new RuntimeException("添加分账接收方失败", e);
        }
    }

    /**
     * 请求分账
     *
     * @param subMchid 子商户号
     * @param transactionId 微信支付订单号
     * @param outOrderNo 商户分账单号
     * @param receivers 分账接收方列表
     * @param unfreezeUnsplit 是否解冻剩余未分资金
     * @return 分账结果
     */
    public OrdersEntity createProfitSharingOrder(String subMchid, String appid, String transactionId, String outOrderNo,
                                                  java.util.List<CreateOrderReceiver> receivers, Boolean unfreezeUnsplit) {
        try {
            CreateOrderRequest request = new CreateOrderRequest();
            request.setSubMchid(subMchid);
            request.setAppid(appid);
            request.setTransactionId(transactionId);
            request.setOutOrderNo(outOrderNo);
            request.setReceivers(receivers);
            request.setUnfreezeUnsplit(unfreezeUnsplit);

            OrdersEntity response = profitsharingService.createOrder(request);
            log.info("请求分账成功，分账单号：{}，微信分账单号：{}", outOrderNo, response.getOrderId());
            return response;
        } catch (Exception e) {
            log.error("请求分账失败，分账单号：{}", outOrderNo, e);
            throw new RuntimeException("请求分账失败", e);
        }
    }

    /**
     * 查询分账结果
     *
     * @param subMchid 子商户号
     * @param transactionId 微信支付订单号
     * @param outOrderNo 商户分账单号
     * @return 分账结果
     */
    public OrdersEntity queryProfitSharingOrder(String subMchid, String transactionId, String outOrderNo) {
        try {
            QueryOrderRequest request = new QueryOrderRequest();
            request.setSubMchid(subMchid);
            request.setTransactionId(transactionId);
            request.setOutOrderNo(outOrderNo);

            OrdersEntity response = profitsharingService.queryOrder(request);
            log.info("查询分账结果成功，分账单号：{}", outOrderNo);
            return response;
        } catch (Exception e) {
            log.error("查询分账结果失败，分账单号：{}", outOrderNo, e);
            throw new RuntimeException("查询分账结果失败", e);
        }
    }
}